#include "stdafx.h"
#include <sstream>
#include <algorithm>
#include <iomanip>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/casts.hpp>
#include "detail/bmock_raw_mem.hpp"
#include "detail/bmock_raw_len.hpp"
#include "detail/bmock_message.hpp"

namespace bmock {
	raw_mem::raw_mem()
		:ptr_(NULL)
		,len_(0)
	{}

	raw_mem::raw_mem(const void *const ptr,const size_t len)
		:ptr_((unsigned char *const)ptr)
		,len_(len)
	{}
	raw_mem &raw_mem::operator =(const raw_mem &rm) {
		ptr_ = rm.ptr_;
		len_ = rm.len_;
		return *this;
	}

	size_t raw_mem::get_length() const {
		return len_;
	}

	const unsigned char *raw_mem::get_ptr() const {
		return ptr_;
	}

	const unsigned char *raw_mem::begin() const {
		return get_ptr();
	}

	unsigned char *raw_mem::begin() {
		return ptr_;
	}

	const unsigned char *raw_mem::end() const {
		return begin()+get_length();
	}

	std::string raw_mem::get_value() const {
		std::ostringstream os;

		if(NULL==ptr_) return NULL_STR;
		hex(os);
		os.fill('0');
		uppercase(os);
		std::for_each(ptr_,ptr_+len_,boost::lambda::var(os)<<std::setw(2)<<boost::lambda::ll_static_cast<unsigned int>(boost::lambda::_1));		
		return os.str();
	}

	bool raw_mem::is_empty() const {
		return (NULL==get_ptr() || 0==get_length());
	}

	std::string raw_mem::get_str_value() const {
		if(is_empty()) return NULL_STR;
		return std::string((const char *)get_ptr());
	}

	struct hex2bin {
		hex2bin(std::string::const_iterator it)
			:ptr_(it)
		{}
		unsigned char operator() () {
			const unsigned char high_nibble = get_nibble()<<4;
			const unsigned char low_nibble  = get_nibble();
			return high_nibble | low_nibble;
		}
	private:
		unsigned char get_nibble() {
			static const char hex_map[] = "0123456789ABCDEF";
			const char *const pC = std::find(hex_map,hex_map+sizeof(hex_map),::toupper(*ptr_++));
			return (unsigned char)(pC - hex_map);
		}

		std::string::const_iterator ptr_;
	};

	void raw_mem::set_value(const size_t v_len,const std::string &v) const {
		if(NULL==ptr_) return;
		std::generate_n(ptr_,v_len,hex2bin(v.begin()));
	}

	bool raw_mem::set_value(const std::string &name,const raw_mem_checker &chk,const std::string &v) const {
		const size_t v_len = v.length()/2;

		if(NULL_STR==v) return true;
		if(chk.check_o_len(name,v_len,get_length())) return true;
		try {
			set_value(v_len,v);
		} catch (...) {
			return true;
		}
		return false;
	}

	in_out_rm_pair::in_out_rm_pair(const void *ptr,const std::string &name,const raw_mem_checker &chk) 
		:std::pair<raw_mem,raw_mem>(raw_mem(0,0),raw_mem(0,0))
	{
		raw_len                    rl;
		if(NULL==ptr) return;
		const unsigned char *const i_ptr = rl.get(ptr,name,chk);
		first = raw_mem(i_ptr,rl.get_i_length());
		second = raw_mem(i_ptr+rl.get_i_length(),rl.get_o_length());
	}

	in_rm::in_rm(const void *ptr,const std::string &name,const raw_mem_checker &chk)
	{
		raw_len                    rl;
		if(NULL==ptr) chk.throw_loc();
		const unsigned char *const i_ptr = rl.get_check(ptr,name,chk);
		rm_=raw_mem(i_ptr,rl.get_i_length());
	}
}